package micropascalcomp.FSA;

import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import micropascalcomp.MP_Token;
import micropascalcomp.Token;

/******************************************
 * 
 * GetIdentifier
 * Takes a string as input and outputs a valid token; with a line and column number.
 * @author Jason Welch, Codi Gann, Jamal Martin
 * 
 *******************************************/
public class IdentifierFSA implements FSABehavior {

    private char letterMin = 97;
    private char letterMax = 122;
    private char capLetterMin = 65;
    private char capLetterMax = 90;
    private char numberMin = 48;
    private char numberMax = 57;
    private char underScore = 95;
    private char newLine = '\n';
    private IdentifierTokens[] ReservedWords = {
        /*0*/new IdentifierTokens("MP_AND", "and"),
        /*1*/ new IdentifierTokens("MP_BEGIN", "begin"),
        /*2*/ new IdentifierTokens("MP_DIV", "div"),
        /*3*/ new IdentifierTokens("MP_DO", "do"),
        /*4*/ new IdentifierTokens("MP_DOWNTO", "downto"),
        /*5*/ new IdentifierTokens("MP_ELSE", "else"),
        /*6*/ new IdentifierTokens("MP_END", "end"),
        /*7*/ new IdentifierTokens("MP_FIXED", "fixed"),
        /*8*/ new IdentifierTokens("MP_FLOAT", "float"),
        /*9*/ new IdentifierTokens("MP_FOR", "for"),
        /*10*/ new IdentifierTokens("MP_FUNCTION", "function"),
        /*11*/ new IdentifierTokens("MP_IF", "if"),
        /*12*/ new IdentifierTokens("MP_INTEGER", "integer"),
        /*13*/ new IdentifierTokens("MP_MOD", "mod"),
        /*14*/ new IdentifierTokens("MP_NOT", "not"),
        /*15*/ new IdentifierTokens("MP_OR", "or"),
        /*16*/ new IdentifierTokens("MP_PROCEDURE", "procedure"),
        /*17*/ new IdentifierTokens("MP_PROGRAM", "program"),
        /*18*/ new IdentifierTokens("MP_READ", "read"),
        /*19*/ new IdentifierTokens("MP_REPEAT", "repeat"),
        /*20*/ new IdentifierTokens("MP_THEN", "then"),
        /*21*/ new IdentifierTokens("MP_TO", "to"),
        /*22*/ new IdentifierTokens("MP_UNTIL", "until"),
        /*23*/ new IdentifierTokens("MP_VAR", "var"),
        /*24*/ new IdentifierTokens("MP_WHILE", "while"),
        /*25*/ new IdentifierTokens("MP_WRITE", "write"),
        /*26*/ new IdentifierTokens("MP_IDENTIFIER", ""),
        /*27*/ new IdentifierTokens("MP_ERROR", "") //I think this should go here		
    };

    /*
     * @see FSABehavior#getToken(java.io.FileReader, java.io.LineNumberReader, int)
     */
    public MP_Token getToken(FileReader file, LineNumberReader inFile, int colNum) {
        char readChar;
        int col = colNum;
        //System.out.print(colNum + ":");

        /*
         * I assume you will never send me something
         * that starts with a number.
         */
        String lex = "";
        try {
            readChar = (char) inFile.read();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        if (readChar == underScore) {
            try {
                inFile.mark(col);
                lex = lex.concat(Character.toString(readChar));
                readChar = (char) inFile.read();
                if (readChar == underScore) {
                    inFile.reset();
                    ReservedWords[27].setLexeme("_");
                    ReservedWords[27].setColNumberAndLineNum(colNum, inFile.getLineNumber());
                    return ReservedWords[27].getMP_Token();
                }
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }

        
        do {
            lex = lex.concat(Character.toString(readChar));
            try {
                inFile.mark(col);
                readChar = (char) inFile.read();
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
            col++;
        } while (isValid(readChar, false));

        try {
            inFile.reset();
        } catch (IOException e) {
            e.printStackTrace();
        }

        /*
         * If an id starts with a underscore then it 
         * cannot be key word so there is no need to 
         * search.
         */
        if (lex.charAt(0) == underScore) {
            ReservedWords[26].setLexeme(lex);
            ReservedWords[26].setColNumberAndLineNum(colNum, inFile.getLineNumber());
            return ReservedWords[26].getMP_Token();
        }

        int index = notBinarySearch(lex);
        if (index == 26) {
            ReservedWords[26].setLexeme(lex);
            ReservedWords[26].setColNumberAndLineNum(colNum, inFile.getLineNumber());
        }

        ReservedWords[index].setColNumberAndLineNum(colNum, inFile.getLineNumber());
        return ReservedWords[index].getMP_Token();

    }//end getToken

    private boolean isValid(char c, boolean isStart) {
        if (c >= letterMin && c <= letterMax) {
            return true;
        }
        if (c >= capLetterMin && c <= capLetterMax) {
            return true;
        }
        if (c == underScore) {
            return true;
        }
        if (!isStart && (c >= numberMin && c <= numberMax)) {
            return true;
        }
        //System.out.print(c);
        return false;
    }

    private int notBinarySearch(String value) {

        for (int i = 0; i <= ReservedWords.length - 1; i++) {
            if (ReservedWords[i].getLexeme().compareTo(value.toLowerCase()) == 0) {
                return i;
            }
        }
        return 26;
    }
}//end class

class IdentifierTokens {

    private String token;
    private String lexeme;
    private int col;
    private int lineNum;

    IdentifierTokens(String t, String l) {
        token = t;
        lexeme = l;
    }

    IdentifierTokens(String t, String l, int c, int line) {
        token = t;
        lexeme = l;
        col = c;
        lineNum = line;
    }

    public String getToken() {
        return token;
    }

    public String getLexeme() {
        return lexeme;
    }

    public int getColNumber() {
        return col;
    }

    public void setToken(String t) {
        token = t;
    }

    public void setLexeme(String l) {
        lexeme = l;
    }

    public void setColNumberAndLineNum(int c, int l) {
        col = c;
        lineNum = l;
    }

    // public MP_Token(Token token, String lexeme, int lineNum, int columnNum)
    public MP_Token getMP_Token() {
        return new MP_Token(Token.valueOf(token), lexeme, lineNum, col);
    }
}
